@@ -1,10 +1,15 @@ |
||
1 | 1 |
$ -> |
2 |
- firstEventCount = null |
|
2 |
+ sinceId = null |
|
3 | 3 |
previousJobs = null |
4 | 4 |
|
5 | 5 |
if $(".job-indicator").length |
6 | 6 |
check = -> |
7 |
- $.getJSON "/worker_status", (json) -> |
|
7 |
+ query = |
|
8 |
+ if sinceId? |
|
9 |
+ '?since_id=' + sinceId |
|
10 |
+ else |
|
11 |
+ '' |
|
12 |
+ $.getJSON "/worker_status" + query, (json) -> |
|
8 | 13 |
for method in ['pending', 'awaiting_retry', 'recent_failures'] |
9 | 14 |
count = json[method] |
10 | 15 |
elem = $(".job-indicator[role=#{method}]") |
@@ -23,16 +28,17 @@ $ -> |
||
23 | 28 |
if elem.is(":visible") |
24 | 29 |
elem.tooltip('destroy').fadeOut() |
25 | 30 |
|
26 |
- firstEventCount = json.event_count unless firstEventCount? |
|
27 |
- if firstEventCount? && json.event_count > firstEventCount |
|
31 |
+ if sinceId? && json.event_count > 0 |
|
28 | 32 |
$("#event-indicator").tooltip('destroy'). |
29 |
- tooltip(title: "Click to reload", delay: 0, placement: "bottom", trigger: "hover"). |
|
33 |
+ tooltip(title: "Click to see the events", delay: 0, placement: "bottom", trigger: "hover"). |
|
34 |
+ find('a').attr(href: json.events_url).end(). |
|
30 | 35 |
fadeIn(). |
31 | 36 |
find(".number"). |
32 |
- text(json.event_count - firstEventCount) |
|
37 |
+ text(json.event_count) |
|
33 | 38 |
else |
34 | 39 |
$("#event-indicator").tooltip('destroy').fadeOut() |
35 | 40 |
|
41 |
+ sinceId ?= json.max_id |
|
36 | 42 |
currentJobs = [json.pending, json.awaiting_retry, json.recent_failures] |
37 | 43 |
if document.location.pathname == '/jobs' && $(".modal[aria-hidden=false]").length == 0 && previousJobs? && previousJobs.join(',') != currentJobs.join(',') |
38 | 44 |
$.get '/jobs', (data) => |
@@ -42,7 +48,3 @@ $ -> |
||
42 | 48 |
window.workerCheckTimeout = setTimeout check, 2000 |
43 | 49 |
|
44 | 50 |
check() |
45 |
- |
|
46 |
- $("#event-indicator a").on "click", (e) -> |
|
47 |
- e.preventDefault() |
|
48 |
- window.location.reload() |
@@ -20,6 +20,20 @@ |
||
20 | 20 |
} |
21 | 21 |
} |
22 | 22 |
|
23 |
+.table-striped > tbody > tr.hl { |
|
24 |
+ &:nth-child(odd) { |
|
25 |
+ > td, > th { |
|
26 |
+ background-color: #ffeecc; |
|
27 |
+ } |
|
28 |
+ } |
|
29 |
+ |
|
30 |
+ &:nth-child(even) { |
|
31 |
+ > td, > th { |
|
32 |
+ background-color: #f9e8c6; |
|
33 |
+ } |
|
34 |
+ } |
|
35 |
+} |
|
36 |
+ |
|
23 | 37 |
table.events { |
24 | 38 |
.payload { |
25 | 39 |
color: #999; |
@@ -1,12 +1,32 @@ |
||
1 | 1 |
class WorkerStatusController < ApplicationController |
2 | 2 |
def show |
3 |
- start = Time.now.to_f |
|
4 |
- render :json => { |
|
5 |
- :pending => Delayed::Job.where("run_at <= ? AND locked_at IS NULL AND attempts = 0", Time.now).count, |
|
6 |
- :awaiting_retry => Delayed::Job.where("failed_at IS NULL AND attempts > 0").count, |
|
7 |
- :recent_failures => Delayed::Job.where("failed_at IS NOT NULL AND failed_at > ?", 5.days.ago).count, |
|
8 |
- :event_count => current_user.events.count, |
|
9 |
- :compute_time => Time.now.to_f - start |
|
3 |
+ start = Time.now |
|
4 |
+ events = current_user.events |
|
5 |
+ |
|
6 |
+ if params[:since_id].present? |
|
7 |
+ since_id = params[:since_id].to_i |
|
8 |
+ events = events.where('id > ?', since_id) |
|
9 |
+ end |
|
10 |
+ |
|
11 |
+ result = events.select('COUNT(id) AS count', 'MIN(id) AS min_id', 'MAX(id) AS max_id').first |
|
12 |
+ count, min_id, max_id = result.count, result.min_id, result.max_id |
|
13 |
+ |
|
14 |
+ case max_id |
|
15 |
+ when nil |
|
16 |
+ when min_id |
|
17 |
+ events_url = events_path(hl: max_id) |
|
18 |
+ else |
|
19 |
+ events_url = events_path(hl: "#{min_id}-#{max_id}") |
|
20 |
+ end |
|
21 |
+ |
|
22 |
+ render json: { |
|
23 |
+ pending: Delayed::Job.where("run_at <= ? AND locked_at IS NULL AND attempts = 0", start).count, |
|
24 |
+ awaiting_retry: Delayed::Job.where("failed_at IS NULL AND attempts > 0").count, |
|
25 |
+ recent_failures: Delayed::Job.where("failed_at IS NOT NULL AND failed_at > ?", 5.days.ago).count, |
|
26 |
+ event_count: count, |
|
27 |
+ max_id: max_id || 0, |
|
28 |
+ events_url: events_url, |
|
29 |
+ compute_time: Time.now - start |
|
10 | 30 |
} |
11 | 31 |
end |
12 | 32 |
end |
@@ -71,4 +71,31 @@ module ApplicationHelper |
||
71 | 71 |
service_label_text(service) |
72 | 72 |
].join.html_safe, class: "label label-default label-service service-#{service.provider}" |
73 | 73 |
end |
74 |
+ |
|
75 |
+ def highlighted?(id) |
|
76 |
+ (@hl ||= |
|
77 |
+ case hl = params[:hl].presence |
|
78 |
+ when nil |
|
79 |
+ ->x { false } |
|
80 |
+ when String |
|
81 |
+ values = hl.split(/,/).flat_map { |i| |
|
82 |
+ case i |
|
83 |
+ when /\A(\d+)\z/ |
|
84 |
+ i.to_i |
|
85 |
+ when /\A(\d+)?-(\d+)?\z/ |
|
86 |
+ ($1 ? $1.to_i : 1)..($2 ? $2.to_i : (1/0.0)) |
|
87 |
+ else |
|
88 |
+ [] |
|
89 |
+ end |
|
90 |
+ } |
|
91 |
+ ->x { |
|
92 |
+ case x |
|
93 |
+ when *values |
|
94 |
+ true |
|
95 |
+ else |
|
96 |
+ false |
|
97 |
+ end |
|
98 |
+ } |
|
99 |
+ end)[id] |
|
100 |
+ end |
|
74 | 101 |
end |
@@ -18,7 +18,7 @@ |
||
18 | 18 |
|
19 | 19 |
<% @events.each do |event| %> |
20 | 20 |
<% next unless event.agent %> |
21 |
- <tr> |
|
21 |
+ <%= content_tag :tr, class: (highlighted?(event.id) ? 'hl' : nil) do %> |
|
22 | 22 |
<td><%= link_to event.agent.name, agent_path(event.agent) %></td> |
23 | 23 |
<td title='<%= event.created_at %>'><%= time_ago_in_words event.created_at %> ago</td> |
24 | 24 |
<td class='payload'><%= truncate event.payload.to_json, :length => 90, :omission => "" %></td> |
@@ -29,12 +29,12 @@ |
||
29 | 29 |
<%= link_to 'Delete', event_path(event), method: :delete, data: { confirm: 'Are you sure?' }, class: "btn btn-default" %> |
30 | 30 |
</div> |
31 | 31 |
</td> |
32 |
- </tr> |
|
32 |
+ <% end %> |
|
33 | 33 |
<% end %> |
34 | 34 |
</table> |
35 | 35 |
</div> |
36 | 36 |
|
37 |
- <%= paginate @events, :theme => 'twitter-bootstrap-3' %> |
|
37 |
+ <%= paginate @events, params: params.slice(:hl), theme: 'twitter-bootstrap-3' %> |
|
38 | 38 |
|
39 | 39 |
<br /> |
40 | 40 |
|
@@ -143,4 +143,36 @@ describe ApplicationHelper do |
||
143 | 143 |
expect(elem).to be_a Nokogiri::XML::Element |
144 | 144 |
end |
145 | 145 |
end |
146 |
+ |
|
147 |
+ describe '#highlighted?' do |
|
148 |
+ it 'understands hl=6-8' do |
|
149 |
+ stub(params).[](:hl) { '6-8' } |
|
150 |
+ expect((1..10).select { |i| highlighted?(i) }).to eq [6, 7, 8] |
|
151 |
+ end |
|
152 |
+ |
|
153 |
+ it 'understands hl=1,3-4,9' do |
|
154 |
+ stub(params).[](:hl) { '1,3-4,9' } |
|
155 |
+ expect((1..10).select { |i| highlighted?(i) }).to eq [1, 3, 4, 9] |
|
156 |
+ end |
|
157 |
+ |
|
158 |
+ it 'understands hl=8-' do |
|
159 |
+ stub(params).[](:hl) { '8-' } |
|
160 |
+ expect((1..10).select { |i| highlighted?(i) }).to eq [8, 9, 10] |
|
161 |
+ end |
|
162 |
+ |
|
163 |
+ it 'understands hl=-2' do |
|
164 |
+ stub(params).[](:hl) { '-2' } |
|
165 |
+ expect((1..10).select { |i| highlighted?(i) }).to eq [1, 2] |
|
166 |
+ end |
|
167 |
+ |
|
168 |
+ it 'understands hl=-' do |
|
169 |
+ stub(params).[](:hl) { '-' } |
|
170 |
+ expect((1..10).select { |i| highlighted?(i) }).to eq [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] |
|
171 |
+ end |
|
172 |
+ |
|
173 |
+ it 'is OK with no hl' do |
|
174 |
+ stub(params).[](:hl) { nil } |
|
175 |
+ expect((1..10).select { |i| highlighted?(i) }).to be_empty |
|
176 |
+ end |
|
177 |
+ end |
|
146 | 178 |
end |